######################################
# Makefile对缩进非常敏感。你需要使用Tab键进行缩进，而不是空格。否则，Make可能会报错。
# target 有些宏是为了方便编译时少输点命令
######################################

######################################
# target
######################################
TARGET = STM32Lab
MCU_TYPE = C6


######################################
# building variables
######################################
# debug build?
DEBUG = 1
# optimization
OPT = -Ofast


#######################################
# paths
#######################################
# Build path
BUILD_DIR = build

######################################
# source
######################################
# C sources
C_SOURCES =  \
Src/system_stm32f1xx.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_gpio.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_adc.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_dac.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_dma.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_rcc.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_utils.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_exti.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_i2c.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_spi.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_pwr.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_tim.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_usart.c \
Src/sys.c \
Src/main.c \
Src/led.c \
Src/spi.c \
Src/font.c \
Src/w25q32.c \
Src/usart1.c \

ifeq ($(DEBUG), 1)
C_SOURCES += \
Drivers/RTT/SEGGER_RTT.c \
Drivers/RTT/SEGGER_RTT_printf.c 
endif

# ASM sources
ASM_SOURCES =  \
startup_stm32f103x6.s

#######################################
# binaries
#######################################
PREFIX = arm-none-eabi-
# The gcc compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx)
# either it can be added to the PATH environment variable.
ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
OD = $(PREFIX)objdump
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
#######################################
# CFLAGS
#######################################
# cpu
CPU = -mcpu=cortex-m3

# fpu
# NONE for Cortex-M0/M0+/M3
# float-abi

# mcu
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)

# macros for gcc
# AS defines
AS_DEFS = 

# C defines
C_DEFS =  \
-DUSE_FULL_LL_DRIVER \
-DHSE_VALUE=8000000 \
-DHSE_STARTUP_TIMEOUT=100 \
-DLSE_STARTUP_TIMEOUT=5000 \
-DLSE_VALUE=32768 \
-DHSI_VALUE=8000000 \
-DLSI_VALUE=40000 \
-DVDD_VALUE=3300 \
-DPREFETCH_ENABLE=1 \
-DSTM32F103x6 \

# 是否为调试模式
ifeq ($(DEBUG), 1)
C_DEFS += -DDEBUG
else
C_DEFS += -DRELEASE
endif


# AS includes
AS_INCLUDES = 

# C includes
C_INCLUDES =  \
-ISrc\
-IDrivers/STM32F1xx_HAL_Driver/Inc \
-IDrivers/CMSIS/Device/ST/STM32F1xx/Include \
-IDrivers/CMSIS/Include \
-IDrivers/RTT


# -Wall: 	这是一个常用的GCC编译选项，用于打开所有“重要”的警告，帮助开发者识别潜在的代码问题。
# -fdata-sections -ffunction-sections: 
# 			这两个选项告诉编译器将数据和函数分别放入不同的段或区域，这样链接器可以更容易地删除未使用的数据或函数，从而优化生成的可执行文件大小。

ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

# -g: 		这个选项告诉编译器生成调试信息，这样开发者可以使用调试器（如GDB）来调试程序。
# 			调试信息包括变量名、函数名、源代码行号等，使得调试过程更加直观和有效。
# -ggdb: 	这个选项也是用于生成调试信息，但它特别针对GDB调试器进行了优化。
# 			使用-ggdb可以确保生成的调试信息与GDB完全兼容，从而获得最佳的调试体验。

ifeq ($(DEBUG), 1)
CFLAGS += -g -ggdb
endif

# -MMD: 	这个选项告诉编译器为每个源文件生成一个依赖文件。这个文件包含源文件和它所依赖的头文件之间的关系。
# 			与-MD选项相比，-MMD只考虑用户提供的头文件，而不考虑系统头文件。
# -MP: 		这个选项会在生成的依赖文件中添加一个伪目标，以确保即使头文件被删除，依赖文件仍然存在。这可以防止构建系统出现意外的中断。
# -MF"$(@:%.o=%.d)": 
# 			这个选项指定了依赖文件的输出位置。
# 			$(@:%.o=%.d)是一个Makefile变量替换操作，它将目标文件名（$@）中的.o扩展名替换为.d。因此，对于每个编译的源文件，
# 			都会生成一个相应的.d依赖文件。
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"

#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = STM32F103C6Tx_FLASH.ld

# libraries
# -lc: 		这个选项指示链接器链接C标准库。C标准库包含了C语言标准中定义的所有函数，
# 			比如printf()、malloc()等。大多数C程序都需要这个库。
# -lm: 		这个选项指示链接器链接数学库，这通常包含了一些数学函数，例如sin()、cos()等。如果你的程序中使用了这些数学函数，你需要链接这个库。
# -lnosys: 	这个选项通常用于嵌入式系统编程，它链接一个特殊的“nosys”库。这个库提供了一些在嵌入式系统中可能没有实现的系统调用的空实现。
# 			这样，如果程序不小心调用了一个未实现的系统调用，链接器不会报错，而是会链接到这个空实现，从而在运行时产生一个错误或警告
LIBS = -lc -lm -lnosys 
LIBDIR = 
# -specs=nano.specs: 
# 			这个选项指定了链接器使用nano.specs文件，这个文件通常用于嵌入式系统，它定义了一些特定的编译器和链接器行为，以优化代码大小和运行效率。
# -Map=$(BUILD_DIR)/$(TARGET).map,: 
# 			这个选项是传递给链接器的，它指示链接器生成一个映射文件，这个文件包含了程序中各个段和符号的地址信息。
# -Wl,--gc-sections: 
# 			这个选项也是传递给链接器的，它指示链接器删除未使用的段，从而优化生成的可执行文件大小。
# 			这是一个常见的用于减小嵌入式系统程序大小的技巧。
# --cref	会在映射文件中添加交叉引用信息。
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections

# default action: build all
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin

#######################################
# build the application
#######################################
# 这行代码定义了一个变量OBJECTS，它包含了所有需要编译的C源文件对应的目标文件。这些目标文件的路径是
# 通过给源文件路径添加一个前缀（即$(BUILD_DIR)/）来得到的。
# $(C_SOURCES:.c=.o): 					这个表达式将所有的.c文件扩展名替换为.o，得到相应的目标文件（对象文件）的名称。
# 										例如，如果C_SOURCES包含src/main.c，那么src/main.c:.c=.o将给出src/main.o。
# $(notdir ...): 						这个函数移除目录部分，只留下文件名。继续上面的例子，notdir src/main.o将得到main.o。
# $(addprefix $(BUILD_DIR)/, ...): 		这个函数给每个文件名添加一个前缀，即$(BUILD_DIR)/。如果BUILD_DIR是build，
# 										那么addprefix build/, main.o将得到build/main.o。
# $(dir $(C_SOURCES)): 					这个表达式提取出每个源文件的目录部分。例如，如果C_SOURCES包含src/main.c和src/utils/helper.c，
# 										那么dir src/main.c src/utils/helper.c将得到src/ . src/utils/。
# $(sort ...): 							这个函数对目录列表进行排序，以确保目录在搜索路径中的顺序是一致的。在上面的例子中，
# 										排序后的结果可能是. src/ src/utils/。
# vpath %.c ...: 						这个指令告诉Make，当它试图找到一个匹配模式（这里是%.c，即任意名称的.c文件）的文件时，
# 										应该在指定的目录中查找。这些目录是按顺序列出的，所以Make会按照这个顺序查找文件。
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))

# 用于生成用户代码的asm清单
MY_CFILES = $(notdir $(wildcard Src/*.c))
MY_OBJS = $(addprefix $(BUILD_DIR)/, $(MY_CFILES:.c=.o))
vpath %.asm $(sort $(dir $(MY_OBJS)))
MY_ASMS = $(addprefix $(BUILD_DIR)/, $(MY_CFILES:.c=.asm))
vpath %.asm $(sort $(dir $(MY_ASMS)))

# list of ASM program objects
# $(BUILD_DIR)/%.o: 这是目标模式，表示在$(BUILD_DIR)目录中的任何.o文件。%是一个通配符，它匹配任何字符序列。
# 					例如，如果$(BUILD_DIR)是./build，那么./build/main.o将与这个模式匹配。
# %.c: 				这是第一个依赖项模式，表示任何.c文件。当Makefile试图构建一个与目标模式匹配的文件时，
# 					它会查找与这个模式匹配的文件。
# Makefile: 		这表明Makefile本身也是一个依赖项。这意味着，如果Makefile更改，那么所有从这个规则构建的
# 					目标都将被重新构建。
# | $(BUILD_DIR): 	这是一个有序依赖（也称为管道文件），表示$(BUILD_DIR)目录必须在处理此规则的命令之前存在。
# 					这确保了构建目录在尝试将对象文件写入其中之前已经存在。
# $(CC): 			这是一个变量，代表C编译器的名称。
# -c: 				这是传递给汇编器的选项，告诉它只进行汇编操作，不进行链接。
# $(CFLAGS): 		这是一个变量，包含传递给汇编器的任何标志或选项。虽然这些标志通常用于C编译器，但它们也可以用于汇编器。
# $<: 				这是一个自动变量，代表规则中的第一个依赖项，即与%.s模式匹配的汇编源文件。
# -o $@: 			这告诉汇编器将输出写入哪里。$@是另一个自动变量，代表规则中的目标，即与$(BUILD_DIR)/%.o模式匹配的对象文件。
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(CC) -c $(CFLAGS) -Wa,-ahlms=$(BUILD_DIR)/$*.lst $< -o $@

$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
	$(AS) -c $(CFLAGS) $< -o $@

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(HEX) $< $@

$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@	

# 将 Src 下的所有代码生成对应的asm文件
$(BUILD_DIR)/%.asm: $(BUILD_DIR)/%.o
	$(OD) -r --source-comment=@ -d -w -S $< > $@

# 这是一个假目标,用以产生上面的调用
$(BUILD_DIR)/$(TARGET).fake : $(MY_ASMS) Makefile
	@echo "all the user src code asm file are generated!"

# 建立BUILD_DIR文件夹
$(BUILD_DIR):
	mkdir $@

#######################################
# clean up
#######################################
.PHONY: clean
clean:
ifeq ($(OS),Windows_NT)  
	-powershell -Command "Remove-Item -Path $(BUILD_DIR) -Recurse"
	-powershell -Command "Remove-Item -Path STM32Cube/Core -Recurse"
	-powershell -Command "Remove-Item -Path STM32Cube/Drivers -Recurse"
	-powershell -Command "Remove-Item -Path STM32Cube/.mxproject -Recurse"
	-powershell -Command "Remove-Item -Path STM32Cube/*.s -Recurse"
	-powershell -Command "Remove-Item -Path STM32Cube/*.ld -Recurse"
	-powershell -Command "Remove-Item -Path STM32Cube/Makefile -Recurse"
else ifeq ($(shell uname -s),Linux)  
	-rm -rdf $(BUILD_DIR)
else ifeq ($(shell uname -s),Darwin)  
	-rm -rdf $(BUILD_DIR)
endif

#######################################
# asm
#######################################
asm: $(BUILD_DIR)/$(TARGET).fake

#######################################
# test
#######################################
test:
	@echo $(OBJECTS)
	@echo "$(MY_ASMS)"
	
#######################################
# download
#######################################
.PHONY: flash
flash:all
# 	echo off
# 	echo r > $(BUILD_DIR)/command.jlink
# 	echo loadbin $(BUILD_DIR)/$(TARGET).hex 0x80000000  >> $(BUILD_DIR)/command.jlink
# 	echo r  >> $(BUILD_DIR)/command.jlink
# 	echo g  >> $(BUILD_DIR)/command.jlink
# 	echo qc >> $(BUILD_DIR)/command.jlink

# ifeq ($(OS),Windows_NT)  
# 	jlink -device STM32F103$(MCU_TYPE) -if SWD -speed 1000 -CommanderScript $(BUILD_DIR)/command.jlink 
# else ifeq ($(shell uname -s),Linux)  
# 	JLinkExe -device STM32F103$(MCU_TYPE) -if SWD -speed 2000 -CommanderScript $(BUILD_DIR)/command.jlink 
# else ifeq ($(shell uname -s),Darwin)  
# 	JLinkExe -device STM32F103$(MCU_TYPE) -if SWD -speed 2000 -CommanderScript $(BUILD_DIR)/command.jlink 
# endif

	pyocd flash -t STM32F103$(MCU_TYPE) -f 10M $(BUILD_DIR)/$(TARGET).hex

#######################################
# reboot
#######################################
# .PHONY: reboot: 			这一行告诉Make，reboot是一个伪目标。即使文件系统中存在一个名为“reboot”的文件，
# 							Make也会执行与该目标相关联的命令。
# reboot:: 					这是目标（target）行，指定了一个名为reboot的目标。
.PHONY: reboot
reboot:
# echo off
# echo r > $(BUILD_DIR)/command.jlink
# echo g  >> $(BUILD_DIR)/command.jlink
# echo qc >> $(BUILD_DIR)/command.jlink

# ifeq ($(OS), Windows_NT)
# 	jlink -device STM32F103$(MCU_TYPE) -if SWD -speed 1000 -CommanderScript $(BUILD_DIR)/command.jlink 
# else ifeq ($(shell uname -s), Linux)  
# 	JLinkExe -device STM32F103$(MCU_TYPE) -if SWD -speed 2000 -CommanderScript $(BUILD_DIR)/command.jlink
# else ifeq ($(shell uname -s), Darwin)  
# 	JLinkExe -device STM32F103$(MCU_TYPE) -if SWD -speed 2000 -CommanderScript $(BUILD_DIR)/command.jlink 
# endif
	pyocd reset -t STM32F103$(MCU_TYPE)

#######################################
# dependencies
#######################################
# $(wildcard PATTERN) 		是一个 Makefile 函数，用于获取与 PATTERN 匹配的所有文件名列表。在这个例子中，
# 							它会查找 $(BUILD_DIR) 目录下所有的 .d 文件。
# $(BUILD_DIR) 				是一个变量，它应该在 Makefile 的其他地方被定义。这个变量通常表示构建的输出目录。
# -include 					是一个 GNU Make 的指令，它会包含指定的文件，但如果文件不存在，它不会产生错误。
# 							这与普通的 include 指令不同，后者在文件不存在时会报错。
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***